package com.hejia.alauda.service;

import com.alibaba.fastjson.JSONObject;
import com.hejia.alauda.entity.*;
import com.hejia.alauda.entity.example.AppVersionExample;
import com.hejia.alauda.entity.example.ParameterExample;
import com.hejia.alauda.entity.example.WarningExample;
import com.hejia.alauda.persistence.mybatis.AppVersionMapper;
import com.hejia.alauda.persistence.mybatis.OrderMapper;
import com.hejia.alauda.persistence.mybatis.ParameterMapper;
import com.hejia.alauda.persistence.mybatis.WarningMapper;
import com.hejia.alauda.utils.Pager;
import com.hejia.alauda.utils.Utils;
import com.hejia.hermes.entity.SMS;
import com.hejia.hermes.service.SMSService;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Created by Johnny(johnny@hejiajinrong.com) on 15/3/3.
 */

@Service
public class ConfigureServiceImpl implements ConfigureService{

    @Resource
    private AppVersionMapper appVersionMapper;

    @Resource
    private ParameterMapper mapper;

    @Resource
    private ParameterMapper parameterMapper;


    @Resource
    private WarningMapper warningMapper;

    @Resource
    private OrderMapper orderMapper;

    @Resource(name = "sendSMS")
    private SMSService smsService;

    /**
     * 订单预警编码
     */
    public static final String SETTING_CODE = "Setting";


    private static final Log Logger = LogFactory
            .getLog(ConfigureServiceImpl.class);


    @Override
    public boolean addAppVersion(AppVersion version) {
        version.setCreateDate(new Date());
        version.setModifyDate(new Date());
        return appVersionMapper.insert(version) > 0;
    }

    @Override
    public boolean modifyAppVersion(AppVersion version) {
    	version.setUrl(version.getUrl().trim());
        return appVersionMapper.updateByPrimaryKey(version) > 0;
    }

    @Override
    public Pager<AppVersion> findAllAppVersion(Pager<AppVersion> pager){
        return findAllAppVersion(pager,new AppVersionExample());
    }

    @Override
    public Pager<AppVersion> findAllAppVersion(Pager<AppVersion> pager,
                                               AppVersionExample example) {
        example.setOrderByClause("id desc");
        pager.setList(appVersionMapper.selectByExample(example, pager));
        pager.setTotalCount(appVersionMapper.countByExample(example));
        return pager;
    }

    @Override
    public AppVersion findLastestAppVersion(String type) {
        if (type == null) {
            return null;
        }
        AppVersionExample exp = new AppVersionExample();
        exp.appendCriterion("type=", type);
        exp.setOrderByClause("createDate desc");
        return appVersionMapper.selectOneByExample(exp);
    }

    @Override
    public boolean saveAppVersion(AppVersion appVersion) {
    	appVersion.setUrl(appVersion.getUrl().trim());
        return appVersionMapper.insert(appVersion)>0;
    }

    @Override
    public AppVersion getAppVersionById(Long id) {
        return appVersionMapper.selectByPrimaryKey(id);
    }

    @Override
    public boolean deleteAppVersion(Long id) {
        AppVersion appVersion = appVersionMapper.selectByPrimaryKey(id);
        return appVersionMapper.deleteByPrimaryKey(appVersion)>0;
    }


    /**
     * 获取系统参数
     *
     * @param category
     * @return
     */
    public Parameter getParameter(Parameter.Category category) {
        ParameterExample exp = new ParameterExample();
        exp.appendCriterion("category =", category);

        return mapper.selectOneByExample(exp);
    }

    /**
     * 获取系统参数列表
     *
     * @param category
     * @return
     */

    public List<Parameter> getParameters(Parameter.Category category) {
        ParameterExample exp = new ParameterExample();
        exp.appendCriterion("category =", category);
        exp.setOrderByClause("createDate desc");
        return mapper.selectByExample(exp, null);
    }

    @Override
    public boolean updateParameter(Parameter param) {
    	param.setModifyDate(new Date());
        return mapper.updateByPrimaryKey(param) > 0;
    }

    @Override
    public Parameter getParameter(Parameter.Category category, String code) {
        ParameterExample exp = new ParameterExample();
        exp.appendCriterion("category =", category);
        exp.appendCriterion("code=",code);
        exp.setOrderByClause("createDate desc");

        return mapper.selectOneByExample(exp);
    }

    @Override
    public boolean saveParameter(Parameter param) {
    	param.setCreateDate(new Date());
    	param.setModifyDate(new Date());
        return mapper.insert(param) > 0;
    }

    @Override
    public Parameter getParameterById(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public boolean deleteParameter(Long id) {
        Parameter parameter = mapper.selectByPrimaryKey(id);
        return mapper.deleteByPrimaryKey(parameter)>0;
    }

    public SplashScreen add(SplashScreen splashScreen) throws Exception {

        splashScreen.setFileName(UUID.randomUUID().toString() + ".png");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(splashScreen);

        Parameter parameter = new Parameter();

        parameter.setCategory(Parameter.Category.SplashScreen);
        parameter.setCode(splashScreen.getCode());
        parameter.setValue(json);
        parameter.setCreateDate(new Date());
        parameter.setModifyDate(new Date());

        parameterMapper.insert(parameter);

        return this.toSplashScreen(parameter);
    }

    @Override
    public SplashScreen toSplashScreen(Parameter parameter) {

        ObjectMapper mapper = new ObjectMapper();
        try {
            SplashScreen splashScreen = mapper.readValue(parameter.getValue(), SplashScreen.class);
            return splashScreen;
        } catch (IOException e) {
            Logger.error("反序列化启动画面对象失败", e);
        }
        return null;
    }

    @Override
    public SplashScreen findSplashScreen(
            String os,
            Integer width,
            Integer height,
            Integer dpi) {

        ParameterExample example = new ParameterExample();
        example.appendCriterion("category =", Parameter.Category.SplashScreen);
        Pager<Parameter> pager = new Pager<>();
        pager.setPageSize(1000);

        List<Parameter> parameters = this.parameterMapper.selectByExample(
                example, pager);

        for (Parameter parameter : parameters) {
            SplashScreen splashScreen = this.toSplashScreen(parameter);
            if(splashScreen == null){
                continue;
            }

            if(!splashScreen.getOs().equals(os)){
                continue;
            }

            if(dpi != null){
                if(splashScreen.getDpi().equals(dpi)){
                    return splashScreen;
                }
            }else{
                if(splashScreen.getWidth().equals(width)
                        && splashScreen.getHeight().equals(height)){
                    return splashScreen;
                }
            }
        }

        return null;
    }

    @Override
    public SplashScreen setSplashScreen(SplashScreen splashScreen) throws Exception {

        ParameterExample example = new ParameterExample();
        example.appendCriterion("category =", Parameter.Category.SplashScreen);
        example.appendCriterion("code =", splashScreen.getCode());

        Parameter parameter = this.parameterMapper.selectOneByExample(example);

        if(parameter == null){
            return this.add(splashScreen);
        }else{
            return this.updateSplashScreen(parameter, splashScreen);
        }
    }

    /**
     * 更新启动画面
     * @param parameter
     * @param splashScreen
     * @return
     * @throws Exception
     */
    private SplashScreen updateSplashScreen(
            Parameter parameter,
            SplashScreen splashScreen) throws Exception {

        ObjectMapper mapper = new ObjectMapper();

        SplashScreen s = this.toSplashScreen(parameter);
        s.setFileName(UUID.randomUUID().toString() + ".png");
        s.setOs(splashScreen.getOs());
        s.setUrl(splashScreen.getUrl());
        s.setWidth(splashScreen.getWidth());
        s.setHeight(splashScreen.getHeight());
        s.setDpi(splashScreen.getDpi());
        s.setStartTime(splashScreen.getStartTime());
        s.setEndTime(splashScreen.getEndTime());

        String json = mapper.writeValueAsString(s);
        parameter.setValue(json);
        parameter.setModifyDate(new Date());
        this.parameterMapper.updateByPrimaryKey(parameter);

        return splashScreen;
    }

    @Override
    public void setDisplayTime(String os, Date startTime, Date endTime) throws Exception {

        List<SplashScreen> splashScreens = this.findAllSplashScreens();
        for( SplashScreen splashScreen : splashScreens){
            if(splashScreen.getOs().equals(os)){

                splashScreen.setStartTime(startTime);
                splashScreen.setEndTime(endTime);

                updateDisplayTime(splashScreen);
            }
        }
    }

    /**
     * 更新显示时间
     * @param splashScreen
     * @throws Exception
     */
    private void updateDisplayTime(SplashScreen splashScreen) throws Exception {

        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(splashScreen);

        ParameterExample example = new ParameterExample();
        example.appendCriterion("category =", Parameter.Category.SplashScreen);
        example.appendCriterion("code =", splashScreen.getCode());

        Parameter parameter = this.parameterMapper.selectOneByExample(example);
        parameter.setValue(json);
        parameter.setModifyDate(new Date());
        this.parameterMapper.updateByPrimaryKey(parameter);

    }

    @Override
    public List<SplashScreen> findAllSplashScreens() {

        List<SplashScreen> splashScreens = new ArrayList<SplashScreen>();

        ParameterExample example = new ParameterExample();
        example.appendCriterion("category =", Parameter.Category.SplashScreen);
        Pager<Parameter> pager = new Pager<>();
        pager.setPageSize(1000);

        List<Parameter> parameters = this.parameterMapper.selectByExample(
                example, pager);

        for (Parameter parameter : parameters) {
            splashScreens.add(this.toSplashScreen(parameter));
        }

        return splashScreens;
    }

    @Override
    public Pager<Warning> findWarning(Pager<Warning> pager) {
        return findWarning(pager, new WarningExample());
    }

    @Override
    public Pager<Warning> findWarning(Pager<Warning> pager,
                                      WarningExample example) {
        example.setOrderByClause("id desc");
        pager.setList(warningMapper.selectByExample(example, pager));
        pager.setTotalCount(warningMapper.countByExample(example));
        return pager;
    }

    @Override
    public boolean deleteWarning(Long id) {
        Warning warning = warningMapper.selectByPrimaryKey(id);
        return warningMapper.deleteByPrimaryKey(warning) > 0;
    }

    @Override
    public boolean saveWarning(Warning warning) {
        return warningMapper.insert(warning) > 0;
    }

    @Override
    public boolean saveParam(WarningSetting ws) throws Exception {
        boolean flag = false;
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(ws);
        Parameter param = getParameter(
                Parameter.Category.Warning, SETTING_CODE);
        if (param == null) {
            Parameter parameter = new Parameter();
            parameter.setCreateDate(new Date());
            parameter.setModifyDate(new Date());
            parameter.setCode(SETTING_CODE);
            parameter.setValue(json);
            parameter.setCategory(Parameter.Category.Warning);
            flag = saveParameter(parameter);
        } else {
            param.setValue(json);
            flag = updateParameter(param);
        }
        return flag;
    }

    @Override
    public void checkFailOrders(){

        WarningSetting setting = this.getWarningSetting();

        // 是否达到预警条件
        int failCount = this.orderMapper.checkFailOrders(setting.getOrderFailNumber());
        Logger.debug("达到预警失败条件订单数：" + failCount);
        if(failCount < 1){
            return;
        }

        // 发送预警
        String message = String.format("您好，快聚财订单已连续出现%s笔交易失败，请及时处理，谢谢！",  setting.getOrderFailNumber());
        Pager<Warning> pager = new Pager<>();
        pager.setPageSize(1000);
        pager = this.findWarning(pager);
        for(Warning warning : pager.getList()){
            try{
                sendWarning(warning, message);
            }catch(Exception e){
                Logger.error("发送预警失败", e);
            }
        }
    }

    /**
     * 发送预警
     * @param warning
     */
    private void sendWarning(Warning warning, String message) {

        if(warning.getWarntype() == Warning.Warntype.AllDay ||
                (warning.getWarntype() == Warning.Warntype.NotAllDay && isInWorkTime())){
            Logger.debug("发送预警短信");
            SMS sms = new SMS();
            sms.setMobile(warning.getPhoneNum());
            sms.setMessage(message);
            sms.setChannel("builtin_notice");
            smsService.sendSMS(sms);
        }
    }

    /**
     * 是否在工作时间
     * @return true - 是， false - 否
     */
    private boolean isInWorkTime() {

        DateTime now = new DateTime();
        int week = now.dayOfWeek().get();
        if(week == 6 || week == 7){
            return false;
        }

        if(now.getHourOfDay() < 9 || now.getHourOfDay() >= 17){
            return false;
        }

        return true;
    }

    @Override
    public WarningSetting getWarningSetting() {

        Parameter parameter = getParameter(Parameter.Category.Warning, SETTING_CODE);
        if(parameter == null){
            return null;
        }

        return JSONObject.parseObject(parameter.getValue(), WarningSetting.class);
    }

    @Override
    public void checkFailOrderBanks() {

        WarningSetting setting = this.getWarningSetting();

        // 是否达到预警条件
        List<String> warningBankNames = new ArrayList<>();
        List<String> bankNames = this.orderMapper.getOrderBankNames();
        Logger.debug("交易银行：" + bankNames);

        for(String bankName : bankNames){
            int failCount = this.orderMapper.checkFailOrderBanks(bankName, setting.getBankFailNumber());
            Logger.debug(String.format("%s达到预警失败条件订单数：%s", bankName, failCount));
            if(failCount > 0){
                warningBankNames.add(bankName);
            }
        }

        Logger.debug("预警失败银行：" + warningBankNames);
        if(warningBankNames.size() < 1){
            return;
        }

        // 发送预警
        String s = "";
        for(String bankName : warningBankNames){
            s += bankName + ",";
        }
        if(StringUtils.isNotEmpty(s)){
            s = s.substring(0,  s.length() - 1);
        }
        String message = String.format("您好，%s已连续出现%s笔交易失败，请及时处理，谢谢！", s, setting.getBankFailNumber());
        Pager<Warning> warningPager = new Pager<>();
        warningPager.setPageSize(1000);
        warningPager = this.findWarning(warningPager);
        for(Warning warning : warningPager.getList()){
            try{
                sendWarning(warning, message);
            }catch(Exception e){
                Logger.error("发送预警失败", e);
            }
        }
    }

	@Override
	public Pager<Parameter> findParametersByPager(ParameterExample example,
			Pager<Parameter> pager) {
		pager.setList(parameterMapper.selectByExample(example, pager));
		pager.setTotalCount(parameterMapper.countByExample(example));
		return pager;
	}

	@Override
	public Map<String, Object> getActivityConfig(String key) {
		Parameter parameter = getParameter(Parameter.Category.Custom, key);
		if(parameter==null)
			return null;
		return Utils.readValue(parameter.getValue());
	}
}
